package org.ccay.excel.importer.service.impl.internal;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Iterator;
import java.util.List;

import javax.xml.stream.XMLStreamException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

import org.ccay.core.exception.ApplicationException;
import org.ccay.core.log.ILogger;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.util.DomUtil;
import org.ccay.core.util.StringUtil;
import org.ccay.excel.importer.validate.ValidatorDef;
import org.ccay.excel.importer.validate.impl.ExcelValidatorManager;
import org.ccay.excel.meta.ColumnMeta;
import org.ccay.excel.meta.ExcelMeta;
import org.ccay.excel.meta.SheetMeta;
import org.ccay.excel.meta.ValidatorMeta;

/**
 * Excel元数据配置解析器。
 * 
 * @author chaowangbang
 * @since Mar 16, 2012
 */
public class ExcelMetaParser {
	/** 日志记录器 **/
	private static ILogger logger = CcayLoggerFactory
			.getLogger(ExcelMetaParser.class);

	/**
	 * 解析输入流获取ExcelMeta
	 * 
	 * @author chaowangbang
	 * @since Mar 19, 2012
	 * @param metaConfig
	 * @return
	 * @throws IOException
	 * @throws XMLStreamException
	 * @throws ApplicationException
	 */
	public ExcelMeta parse(InputStream metaConfig) throws XMLStreamException,
			ApplicationException {
		return parse(new InputSource(metaConfig));
	}
	
	 
	/**
	 * 解析输入流获取ExcelMeta
	 * @param metaConfig
	 * @return
	 * @throws XMLStreamException
	 * @throws ApplicationException
	 */
	public ExcelMeta parse(Reader metaConfig) throws XMLStreamException,
			ApplicationException {
		return parse(new InputSource(metaConfig));
	}
	
	/**
	 * 解析输入流获取ExcelMeta
	 * @param inputSource
	 * @return
	 * @throws XMLStreamException
	 * @throws ApplicationException
	 */
	public ExcelMeta parse(InputSource inputSource) throws XMLStreamException,	ApplicationException {
		// ExcelMeta对象用于储存Excel元数据
		ExcelMeta excelMeta = null;
		try {
			Document doc = DomUtil.loadDocument(inputSource, null);
			Element excelElem = doc.getDocumentElement();
			if (excelElem != null) {
				excelMeta = createExcelMeta(excelElem);
			}
		} catch (IOException ioe) {
			logger.error(ioe);
		}
		return excelMeta;
	}

	/**
	 * 构造ExcelMeta对象
	 * 
	 * @author chaowangbang
	 * @since 2012-3-19
	 * @param reader
	 * @return
	 * @throws ApplicationException
	 */
	private ExcelMeta createExcelMeta(Element elem) throws ApplicationException {
		// 设置Excel的元数据
		// 初始化Excel对象
		// 导出Excel的类型标识
		ExcelMeta excelMeta = new ExcelMeta(elem.getAttribute("id"));
		//设置属性
		setProperties(elem, excelMeta);
		List<Element> validatorsElems = DomUtil.getChildElementsByTagName(elem,
				"validators");
		List<Element> sheetElems = DomUtil.getChildElementsByTagName(elem,
				"sheet");
		if (sheetElems != null && !sheetElems.isEmpty()) {
			processSheet(excelMeta, sheetElems);
		}

		if (validatorsElems != null && !validatorsElems.isEmpty()) {
			processValidators(validatorsElems);
		}
		return excelMeta;
	}

	/**
	 * 设置属性
	 * @param elem
	 * @param excelMeta
	 */
	private void setProperties(Element elem, ExcelMeta excelMeta) {
		// 导出时允许的最大错误数
		int maxAllowedErrors = 0;
		if (!StringUtil.isNullOrEmpty(elem.getAttribute("maxAllowedErrors"))) {
			maxAllowedErrors = Integer.decode(elem
					.getAttribute("maxAllowedErrors"));
		}
		excelMeta.setMaxAllowedErrors(maxAllowedErrors);
		// 导出权限点
		excelMeta.setPrivilege(elem.getAttribute("privilege"));

		// 导出的文件名
		excelMeta.setFileName(elem.getAttribute("fileName"));
		
		// 错误配置文件
		excelMeta.setErrorDisplay(elem.getAttribute("errorDisplay"));
		// 动态修改数据提供类
		excelMeta.setDynamicProvider(elem.getAttribute("dynamicProvider"));
		//是否不启用事务
		excelMeta.setNoTransaction(elem.getAttribute("noTransaction"));
	}

	/**
	 * @param validatorsElems
	 */
	private void processValidators(List<Element> validatorsElems) {
		for (Iterator<Element> iter = validatorsElems.iterator(); iter
				.hasNext();) {
			Element validatorElem = iter.next();
			createValidatorDef(validatorElem);
		}
	}

	/**
	 * 解析validator
	 * 
	 * @param validatorElem
	 */
	private void createValidatorDef(Element validatorElem) {
		List<Element> validatorElems = DomUtil.getChildElementsByTagName(
				validatorElem, "validator");
		if (validatorElems != null && !validatorElems.isEmpty()) {
			for (Iterator<Element> iter = validatorElems.iterator(); iter
					.hasNext();) {
				Element elem = iter.next();
				ValidatorDef validatorDef = new ValidatorDef(
						elem.getAttribute("name"));
				// 设置类名
				String className = elem.getAttribute("class");
				// 设置messageKey
				String messageKey = elem.getAttribute("messageKey");
				validatorDef.setClassName(className);
				validatorDef.setMessageKey(messageKey);
				// 注册validator
				ExcelValidatorManager.registerValidator(validatorDef);
			}
		}

	}

	/**
	 * @param excelMeta
	 * @param sheetElems
	 * @throws ApplicationException
	 */
	private void processSheet(ExcelMeta excelMeta, List<Element> sheetElems)
			throws ApplicationException {
		for (Iterator<Element> iter = sheetElems.iterator(); iter.hasNext();) {
			Element sheetElem = iter.next();
			excelMeta.addSheetMeta(createSheetMeta(sheetElem));
		}
	}

	/**
	 * 构造SheetMeta对象
	 * 
	 * @author chaowangbang
	 * @since 2012-3-19
	 * @param reader
	 * @return
	 * @throws ApplicationException
	 */
	private SheetMeta createSheetMeta(Element elem) throws ApplicationException {
		// 设置Sheet的元数据的
		// Sheet的名称
		SheetMeta sheetMeta = new SheetMeta(elem.getAttribute("name"));
		// 标题行的索引号
		int titleRowIndex = Integer.decode(elem.getAttribute("titleRowIndex"));
		sheetMeta.setTitleRowIndex(titleRowIndex);
		// 数据起始行的索引号
		int dataRowIndex = Integer.decode(elem.getAttribute("dataRowIndex"));
		sheetMeta.setDataRowIndex(dataRowIndex);
		
		String endDataRowIndex = elem.getAttribute("endDataRowIndex");
		// 数据结束行的索引号
		if (!StringUtil.isNullOrEmpty(endDataRowIndex)) {
			sheetMeta.setEndDataRowIndex(Integer.decode(endDataRowIndex));
		}
		// 行数据对应的VO名称
		sheetMeta.setVoBeanName(elem.getAttribute("voClassName"));
		// Sheet的数据消费者对应的Spring Bean
		sheetMeta.setConsumerBean(elem.getAttribute("consumerBean"));
		sheetMeta.setExtendedConsumer(elem.getAttribute("extendedConsumer"));
		String displayName = elem.getAttribute("displayName");
		sheetMeta.setDynamicItem(elem.getAttribute("dynamicItem"));
		if(StringUtil.isNullOrEmpty(displayName)){
			sheetMeta.setDisplayName(sheetMeta.getPartSheetName());
		}else{
			//增加显示名称
			sheetMeta.setDisplayName(displayName);
		}
		
		// 设置导出和导入的批次处理条数
		String batchSize = elem.getAttribute("batchSize");
		if (!StringUtil.isNullOrEmpty(batchSize)) {
			sheetMeta.setBatchSize(Integer.decode(batchSize));
		}
		
		//跨度
		String exportColumnWidth = elem.getAttribute("exportColumnWidth");
		if(!StringUtil.isNullOrEmpty(exportColumnWidth)){
			sheetMeta.setExportColumnWidth(Integer.decode(exportColumnWidth));
		}
		//获取高度
		String  exportRowHeight = elem.getAttribute("exportRowHeight");
		if(!StringUtil.isNullOrEmpty(exportRowHeight)){
			sheetMeta.setExportRowHeight(Integer.decode(exportRowHeight));
		}
		//获取错误信息回写列 如："A" "B"
		String errorDisplayRow = elem.getAttribute("errorDisplayRow");
		if(!StringUtil.isNullOrEmpty(errorDisplayRow)){
			sheetMeta.setErrorDisplayRow(errorDisplayRow);
		}
		//是否去除空格
		sheetMeta.setColumnWipe(elem.getAttribute("columnWipe"));

		List<Element> columnElems = DomUtil.getChildElementsByTagName(elem,
				"column");
		if (columnElems == null || columnElems.isEmpty()) {
			return sheetMeta;
		}
		for (Iterator<Element> iter = columnElems.iterator(); iter.hasNext();) {
			Element columnElem = iter.next();
			sheetMeta.addColumnMeta(createColumnMeta(columnElem));
		}
		return sheetMeta;
	}

	/**
	 * 构造ColumnMeta对象
	 * 
	 * @author chaowangbang
	 * @since 2012-3-19
	 * @param reader
	 * @return
	 */
	private ColumnMeta createColumnMeta(Element elem) {
		// 设置Column的元数据
		// 设置Column的名称
		ColumnMeta columnMeta = new ColumnMeta(elem.getAttribute("name"));
		// 设置Column的数据类型
		columnMeta.setType(elem.getAttribute("type"));
		// 设置Column对应的VO属性名称
		columnMeta.setFieldName(elem.getAttribute("fieldName"));
		
		columnMeta.setAnnotation(elem.getAttribute("annotation"));
		
		columnMeta.setExtentionBean(elem.getAttribute("extentionBean"));
		
		columnMeta.setDynamicItem(elem.getAttribute("dynamicItem"));
		
		String displayName = elem.getAttribute("displayName");
		if(StringUtil.isNullOrEmpty(displayName)){
			columnMeta.setDisplayName(columnMeta.getName());
		}else{
			//增加显示名称
			columnMeta.setDisplayName(displayName);
		}
		List<Element> validatorElems = DomUtil.getChildElementsByTagName(elem,
				"validator");
		if (validatorElems == null || validatorElems.isEmpty()) {
			return columnMeta;
		}
		for (Iterator<Element> iter = validatorElems.iterator(); iter.hasNext();) {
			Element validatorElem = iter.next();
			columnMeta.addValidator(createValidatorMeta(validatorElem));
		}
		return columnMeta;
	}

	/**
	 * 构造ValidatorMeta对象
	 * 
	 * @author chaowangbang
	 * @since 2012-3-19
	 * @param reader
	 * @return
	 */
	private ValidatorMeta createValidatorMeta(Element elem) {
		// 设置Validator的类型
		String type = elem.getAttribute("type");
		// 设置验证类型
		ValidatorMeta validatorMeta = new ValidatorMeta(type);
		processParam(elem, validatorMeta);
		processMessage(elem, validatorMeta);
		return validatorMeta;
	}

	/**
	 * 
	 * @param elem
	 * @param validatorMeta
	 */
	private void processMessage(Element elem, ValidatorMeta validatorMeta) {
		List<Element> msgElems = DomUtil.getChildElementsByTagName(elem,
				"message");
		if (msgElems != null && !msgElems.isEmpty()) {
			for (Iterator<Element> iter = msgElems.iterator(); iter.hasNext();) {
				Element msgElem = iter.next();
				validatorMeta.addParam("messageKey",
						msgElem.getAttribute("key"));
				validatorMeta.addParam("defaultMessage",
						msgElem.getTextContent());
			}
		}
	}

	/**
	 * 
	 * @param elem
	 * @param validatorMeta
	 */
	private void processParam(Element elem, ValidatorMeta validatorMeta) {
		List<Element> paramElems = DomUtil.getChildElementsByTagName(elem,
				"param");
		if (paramElems != null && !paramElems.isEmpty()) {
			for (Iterator<Element> iter = paramElems.iterator(); iter.hasNext();) {
				Element paramElem = iter.next();
				validatorMeta.addParam(paramElem.getAttribute("name"),
						paramElem.getTextContent());
			}
		}
	}
}
